package com.yxsk.relay.job.component.endpoint.facade.net;

import com.yxsk.relay.job.component.common.exception.net.HandlerNotFoundException;
import com.yxsk.relay.job.component.common.net.handler.message.MessageServerHandler;
import com.yxsk.relay.job.component.common.net.message.NetRequestMessage;
import com.yxsk.relay.job.component.common.net.serialization.MessageEntitySerializer;
import com.yxsk.relay.job.component.endpoint.facade.JobHeadman;
import com.yxsk.relay.job.component.endpoint.facade.anno.FacadeUriMapping;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description 网络请求报文处理器
 * @Author 11376
 * @CreateTime 2019/9/6 18:36
 */
@Slf4j
public class RelayJobEndpointFacadeHandler implements MessageServerHandler {

    private JobHeadman headman;

    private Map<String, Method> INVOKE_METHOD_CACHE;

    private MessageEntitySerializer serializer;

    public RelayJobEndpointFacadeHandler(JobHeadman headman, MessageEntitySerializer serializer) {
        this.headman = headman;
        this.serializer = serializer;

        this.init();
    }

    private void init() {
        if (this.headman == null) {
            throw new IllegalArgumentException("Job headman is null.");
        }

        this.INVOKE_METHOD_CACHE = new HashMap<>();

        Class<? extends JobHeadman> headmanClass = this.headman.getClass();
        Method[] methods = headmanClass.getMethods();
        for (Method method : methods) {
            FacadeUriMapping uriMapping = method.getAnnotation(FacadeUriMapping.class);
            if (uriMapping == null) {
                // 查找父类
                Class<?> superclass = headmanClass.getSuperclass();
                if (superclass == Object.class) {
                    superclass = JobHeadman.class;
                }
                try {
                    Method superclassMethod = null;
                    while (true) {
                        superclassMethod = superclass.getMethod(method.getName(), method.getParameterTypes());
                        uriMapping = superclassMethod.getAnnotation(FacadeUriMapping.class);
                        if (uriMapping != null) {
                            this.INVOKE_METHOD_CACHE.put(uriMapping.value(), method);
                            break;
                        }
                        superclass = superclass.getSuperclass();
                        if (superclass == null) {
                            break;
                        } else if (superclass == Object.class) {
                            superclass = JobHeadman.class;
                        }
                    }

                } catch (NoSuchMethodException e) {
                    // Nothing.
                }
            } else {
                this.INVOKE_METHOD_CACHE.put(uriMapping.value(), method);
            }
        }

        if (log.isInfoEnabled()) {
            this.INVOKE_METHOD_CACHE.entrySet().stream().forEach(entry -> log.info("Bind uri mapping: {} -> {}", entry.getKey(), entry.getValue().getDeclaringClass().getName() + "." + entry.getValue().getName()));
        }
    }

    @Override
    public byte[] handle(NetRequestMessage requestMessage) throws Exception {
        // 查找方法
        Method method = this.INVOKE_METHOD_CACHE.get(requestMessage.getUri());
        if (method == null) {
            throw new HandlerNotFoundException(requestMessage.getUri());
        }

        // 获取方法参数
        Class<?>[] parameterTypes = method.getParameterTypes();

        Object result;
        if (ArrayUtils.isEmpty(parameterTypes)) {
            // 直接执行方法
            result = method.invoke(this.headman);
        } else {
            // 反序列化参数
            byte[] message = requestMessage.getMessage();
            Object parameter = message == null ? null : this.serializer.deserialize(message, parameterTypes[0]);

            result = method.invoke(this.headman, parameter);
        }

        // 返回报文处理结果
        return result == null ? null : this.serializer.serialize(result);
    }

}
